package org.vnl.platform.impl.openjpa.std;

import java.util.LinkedList;

import javax.persistence.EntityManager;


import org.apache.log4j.Logger;
import org.vnl.platform.orm.TransactionErrCode;
import org.vnl.platform.orm.TransactionException;

/**
 * 若只有使用到一個EntityManager則只會像單純的單一Transaction,
 * 
 * 若多於一個,則有點像JTA,多DB的樣子,不過有個缺點,無法像XA那樣做two phase commit, 在commit或rollback不完全時,資料會出事
 * 
 * @author Vaughan
 *
 */
public class TomcatEntityTransactionManager implements IEntityTransactionManager {
    
    private static final Logger logger = Logger.getLogger(TomcatEntityTransactionManager.class);
    
    private static TomcatEntityTransactionManager instance;
    
    private LinkedList<String> entityManagerNames;
    
    private TomcatEntityTransactionManager() {
        entityManagerNames = new LinkedList<String>();
    }
    
    public void addEntityManagerName(String name) {
        this.entityManagerNames.add(name);
    }
    
    public void clear() {
        this.entityManagerNames.clear();
    }
    
    public static TomcatEntityTransactionManager getInstance() {
        if (instance == null) {
            instance = new TomcatEntityTransactionManager();
        }
        return instance;
    }

    public void beginTransaction() {
        for (String name : entityManagerNames) {
            EntityManager em = EntityManagerContanier.getInstance().getCurrentEntityManager(name);
            if (!em.getTransaction().isActive()) {
                em.getTransaction().begin();
            }
        }
    }
    
    public void rollback() {
        for (String name : entityManagerNames) {
            EntityManager em = EntityManagerContanier.getInstance().getCurrentEntityManager(name);
            if (em.getTransaction().isActive()) {
                try {
                    em.getTransaction().rollback();
                } catch (Throwable e) {
                    logger.fatal("Transaction rollback error. Data may by inconsistence!");
                    throw new TransactionException(TransactionErrCode.TRANSACTION_ROLLBACK_ERROR, e);
                }
            }
        }
    }
    
    public void commit() {
        boolean rollback = false;
        for (String name : entityManagerNames) {
            EntityManager em = EntityManagerContanier.getInstance().getCurrentEntityManager(name);
            if (em.getTransaction().isActive()) {
                if (em.getTransaction().getRollbackOnly()) {
                    rollback = true;
                    break;
                }
            }
        }
        if (rollback) {
            logger.fatal("Transaction cannot commit. It must rollback due to getRollbackOnly is true");
            rollback();
            return;
        }
        for (String name : entityManagerNames) {
            EntityManager em = EntityManagerContanier.getInstance().getCurrentEntityManager(name);
            if (em.getTransaction().isActive()) {
                try {
                    em.getTransaction().commit();
                } catch (Throwable e) {
                    logger.fatal("Transaction commit error. Data may by inconsistence!");
                    throw new TransactionException(TransactionErrCode.TRANSACTION_COMMIT_ERROR, e);
                }
            }
        }
    }
}
